#!/usr/bin/env python

"""
 Converts a Pyjon parse tree into JooScript syntax.

 Created in Jan 2011 by Evgen Burzak <buzzilo@gmail.com>
"""

import sys, copy
import defs, node2js, jooparser

joo = jooparser.JooParser
NodeToJS = node2js.NodeToJS

class JooNodeToJS(NodeToJS): 

    def __init__(self, parent=None): 
        NodeToJS.__init__(self, parent)
        self.parentContext = parent

    unit_obj = "unit"
    root_obj = "root"
    glob_obj = "glob"
    class_obj = "__class__"
    group_obj = "__group__"

    def convert_class(self,n,i,c):
        self.check(n,attrs=["classForm","ancestors","body"], optattrs=["namespace","name","role"])
        ctx = JooClass(self)
        return ctx.o(n,i,c)

    def convert_package(self,n,i,c):
        self.check(n,attrs=["group","body"])
        group = ".".join(n.group)
        return "%s.Package(%r,function(%s)%s)" % (self.unit_obj, group, 
                    self.group_obj, self.o(n.body,i,c))

    def convert_let(self,n,i,c):
        n.value = "var"
        return NodeToJS.convert_var(self,n,i,c)

    def convert_function(self,n,i,c):
        self.check(n,attrs=["functionForm","params","body"], optattrs=["name"])
        ctx = JooFunc(self)
        return ctx.o(n,i,c)

    def convert_script(self,n,i,c):
        script = NodeToJS.convert_script(self,n,i,c,optattrs=["clsDecls"])
        if n.rootNode:
            return "$Unit(%r,%r,function(%s)%s);" % ("path","file",
                            ",".join((self.unit_obj,self.root_obj,self.glob_obj)), script)
        return script

    def convert_import(self,n,i,c):
        self.check(n,attrs=["groups"])
        groups = ",".join(["%r" % ".".join(x) for x in n.groups])
        return "%s.Import(%s)" % (self.unit_obj, groups)

    def convert_self(self,n,i,c):
        return n.value

    def convert_identifier(self, n, i, c):
        self.check(n,optattrs=["varType"])
        return NodeToJS.convert_identifier(self,n,i,c)

class JooFunc(JooNodeToJS): 
    """
    Converts a parsed function node into JS
    """
    def __init__(self, parentContext): 
        JooNodeToJS.__init__(self, parentContext)

    def convert_function(self,n,i,c):
        self.check(n,attrs=["functionForm","params","body"],
                        optattrs=["constructor","name","namespace","restParams","retType"])
        funName = n.name or ""
        if n.functionForm == joo.DECLARED_FORM:
            return "function %s(%s) %s" % (funName,
                    ", ".join(param for param in n.params),
                    self.o(n.body,i,c))
        else:
            funBody = self.o(n.body,i,c)
            if n.functionForm == joo.LAMBDA_FORM:
                funBody = "{return %s}" % funBody
            return "function %s(%s)%s" % (funName, ", ".join(n.params), funBody)

# FIXME extend class parser for that!
class JooClass(JooNodeToJS): 
    """
    Converts a parsed class node into JS
    """
    def __init__(self, parentContext): 
        JooNodeToJS.__init__(self, parentContext)
        self.namespace = []

    def convert_class(self,n,i,c):
        self.check(n,attrs=["classForm","ancestors","body"], optattrs=["name","role","namespace"])
        className = getattr(n, "name", "")
        return "%s.buildClass(%r,%s,function(%s)%s)" % (
                    self.unit_obj, className, 
                    n.ancestors and "[%s]" % self.parentContext.o(n.ancestors,i,c) or "[]", 
                    self.class_obj, 
                    n.body and self.o(n.body,i,c) or "{}")

    def decl(self, namespace, name, value, varType, retType):
        ns = []
        override = False
        for x in namespace:
            if x == "override": override = True
            else: ns.append("__%s__" % x.upper())

        flags = len(ns) and "|".join(ns) or 0
        add = override and "__set__" or "__add__"

        return "this.%s(%s,%s,%s,%s,%s)" % (add, flags, name, value, varType, retType)

    def convert_list(self, n, i, c):
        self.check(n,subnodes=len(n))
        if self.stack[-2]["node"] == "SEMICOLON":
            names = []
            values = []
            varTypes = []
            retTypes = []
            for x in n:
                assert x.type in ("IDENTIFIER", "ASSIGN", "FUNCTION")
                type_ = x.type.lower()
                if type_ == "identifier": type_ = type_[:2]
                type_ = "convert_" + type_
                name, value, varType, retType = getattr(self, type_)(x,i,c,list_=True)
                names.append(name)
                values.append(value)
                varTypes.append(varType)
                retTypes.append(retType)
                
            return self.decl(self.namespace, "[%s]" % ",".join(names), 
                        "[%s]" % ",".join(values), "[%s]" % ",".join(varTypes), 
                        "[%s]" % ",".join(retTypes))
        else:
            for x in n:
                return self.o(x,n,i,c)
    
    convert_comma = convert_list

    def convert_id(self, n, i, c, list_=False):
        name = repr(n.value)
        varType = getattr(n, "varType", None)
        value = retType = "null"
        namespace = self.namespace

        if varType: varType = self.unit_obj + "." + varType 
        else: varType = "null"

        if list_:
            return [name, value, varType, retType]
        else:
            return self.decl(namespace, name, value, varType, retType)

    def convert_assign(self, n, i, c, list_=False):
        if not list_:
            self.check(n,subnodes=2)
        assert n[0].type == "IDENTIFIER", "identifier expected"
        name = repr(n[0].value)
        varType = getattr(n[0], "varType", None)
        # note: this is the same context, so that I can convert gettings values
        # from the class props
        if n[1].type == "FUNCTION":
            funName, value, varType, retType = self.convert_function(n[1],i,c,list_=True)
            value = value
            # FIXME
            #retType = ""
        else:
            value = self.o(n[1],i,c)

        namespace = self.namespace

        if varType: varType = self.unit_obj + "." + varType 
        else: varType = "null"

        retType = "null"

        if list_:
            return [name, value, varType, retType]
        else:
            return self.decl(namespace, name, value, varType, retType)

    def convert_function(self, n, i, c, list_=False):
        if not list_:
            self.check(n,attrs=["body","functionForm","name","params"],
                        optattrs=["constructor","restParams","retType","namespace","lambda"])
        if getattr(n, "constructor", None):
            assert n.functionForm == joo.DECLARED_FORM
            n.name = "__new__"
        name = repr(getattr(n, "name", None))
        n.name = None
        retType = getattr(n, "retType", None)
        if retType:
            retType = self.unit_obj + "." + retType 
        else: 
            retType = "null"
        params = ["self"]
        params.extend(n.params)
        n.params = params
        fun_ctx = JooFunc(self)
        value = fun_ctx.o(n,i,c) # optattrs=["restParams","retType"]
        varType = "Function"
        namespace = getattr(n, "namespace", [])

        if list_:
            return [name, value, varType, retType]
        else:
            return self.decl(namespace, name, value, varType, retType)

    def convert_semicolon(self, n, i, c):
        self.check(n,attrs=["expression"],optattrs=["namespace"])
        self.namespace = getattr(n, "namespace", [])
        # can be any expression, but some must be converted into special calls
        if n.expression.type == "IDENTIFIER":
            return self.convert_id(n.expression,i,c)
        else:
            return self.o(n.expression,i,c)
            #raise node2js.ProgrammerError, "Unknown expression " + n.expression.type

def convert(parsetree, include_props=False):
    js = JooNodeToJS()
    return js.convert(parsetree, "", {}) + "\n"
